#ifndef __U2Circle_H_
#define __U2Circle_H_


#include "U2PreRequest.h"
#include "U2Vector2.h"


U2EG_NAMESPACE_BEGIN


/** A sphere primitive, mostly used for bounds checking. 
@remarks
    A sphere in math texts is normally represented by the function
    x^2 + y^2 = r^2 (for circle's centered on the origin). U2 stores circles
    simply as a center point and a radius.
*/
class _U2Share U2Circle
{
protected:
    u2real mRadius;
    U2Vector2 mCenter;

public:
    /** Standard constructor - creates a unit sphere around the origin.*/
    U2Circle() : mRadius(1.0), mCenter(U2Vector2::ZERO) {}
    /** Constructor allowing arbitrary spheres. 
        @param center The center point of the sphere.
        @param radius The radius of the sphere.
    */
    U2Circle(const U2Vector2& center, u2real radius)
        : mRadius(radius), mCenter(center) {}

    /** Returns the radius of the sphere. */
    u2real getRadius(void) const { return mRadius; }

    /** Sets the radius of the sphere. */
    void setRadius(u2real radius) { mRadius = radius; }

    /** Returns the center point of the sphere. */
    const U2Vector2& getCenter(void) const { return mCenter; }

    /** Sets the center point of the sphere. */
    void setCenter(const U2Vector2& center) { mCenter = center; }

	/** Returns whether or not this sphere intersects another sphere. */
	bool intersects(const U2Circle& s) const
	{
        return (s.mCenter - mCenter).squaredLength() <=
            U2Math::Sqr(s.mRadius + mRadius);
	}
	/** Returns whether or not this sphere intersects a box. */
    /**********************@@@@@@@@@@@@@@@@@@@@@
	bool intersects(const AxisAlignedBox& box) const
	{
		return U2Math::intersects(*this, box);
	}
    */
	/** Returns whether or not this sphere intersects a plane. */
    /**********************@@@@@@@@@@@@@@@@@@@@@
	bool intersects(const Plane& plane) const
	{
		return U2Math::intersects(*this, plane);
	}
    */
	/** Returns whether or not this sphere intersects a point. */
	bool intersects(const U2Vector2& v) const
	{
        return ((v - mCenter).squaredLength() <= U2Math::Sqr(mRadius));
	}
	/** Merges another U2Circle into the current sphere */
	void merge(const U2Circle& oth)
	{
		U2Vector2 diff =  oth.getCenter() - mCenter;
		u2real lengthSq = diff.squaredLength();
		u2real radiusDiff = oth.getRadius() - mRadius;
		
		// Early-out
		if (U2Math::Sqr(radiusDiff) >= lengthSq) 
		{
			// One fully contains the other
			if (radiusDiff <= 0.0f) 
				return; // no change
			else 
			{
				mCenter = oth.getCenter();
				mRadius = oth.getRadius();
				return;
			}
		}
		
		u2real length = U2Math::Sqrt(lengthSq);
		
		U2Vector2 newCenter;
		u2real newRadius;
		if ((length + oth.getRadius()) > mRadius) 
		{
			u2real t = (length + radiusDiff) / (2.0f * length);
			newCenter = mCenter + diff * t;
		} 
		// otherwise, we keep our existing center
		
		newRadius = 0.5f * (length + mRadius + oth.getRadius());
		
		mCenter = newCenter;
		mRadius = newRadius;
	}
};


U2EG_NAMESPACE_END


#endif

